package com.springBoot.springBootSysCore.common.service.jpaQuery;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;

import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.ListJoin;
import javax.persistence.criteria.Path;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

import org.apache.commons.lang.StringUtils;
import org.springframework.data.jpa.domain.Specification;

import com.springBoot.springBootSysCore.common.utils.SpringContextHolder;


@SuppressWarnings({ "rawtypes", "unchecked" })
public class DynamicSpecifications {
	public static <T> Specification<T> bySearchFilter(Collection<SearchFilter> andfilters){
		return bySearchFilter(andfilters, null);
	}
	
	public static <T> Specification<T> bySearchFilter(final Collection<SearchFilter> andfilters,final Collection<SearchFilter> orfilters) {
		return new Specification<T>() {
			@Override
			public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query, CriteriaBuilder builder) {
 				List<Predicate> predicates = new ArrayList<>();
 				List<Predicate> andPredicates = getPredicates(andfilters, root, builder);
 				if(andPredicates.size()>0){
 					predicates.addAll(andPredicates);
 				}
 				List<Predicate> orPredicates = getPredicates(orfilters, root, builder);
 				// 将所有条件用 or 联合起来并加入到 最终条件中
				if(orPredicates.size()>0){
					predicates.add(builder.or(orPredicates.toArray(new Predicate[orPredicates.size()])));
				}
//				return builder.and(predicates.toArray(new Predicate[predicates.size()]));
				query.where(predicates.toArray(new Predicate[predicates.size()]));
				query.distinct(true);
//				query.getGroupRestriction();
				return query.getRestriction();
			}
		};
	}
	/**
	 * 添加条件参数
	 * @author PSC
	 * @param filter
	 * @param builder
	 * @param predicates
	 * @param expression
	 * @return
	 */
	public static List<Predicate> addPredicate(SearchFilter filter,CriteriaBuilder builder,List<Predicate> predicates,
			Path expression,ListJoin listJoin,String taxonNature,String taxonNatureMore){
		if(listJoin!=null){
			switch (filter.operator) {
				case EQ:
					if(org.apache.commons.lang3.StringUtils.isNoneBlank(taxonNatureMore)){
						predicates.add(builder.equal(listJoin.get(taxonNature).get(taxonNatureMore).as(String.class), filter.value));
					}else {
						predicates.add(builder.equal(listJoin.get(taxonNature).as(String.class), filter.value));
					}
					break;
				case IN:
					String val = String.valueOf(filter.value);
					predicates.add(listJoin.get(taxonNature).as(String.class).in(new ArrayList<String>(Arrays.asList(val.split(",")))));
					break;
				default:
					break;
			}	
		}else{
			switch (filter.operator) {
				case ISTRUE:
					predicates.add(builder.isTrue(expression));
					break;
				case ISFALSE:
					predicates.add(builder.isFalse(expression));
					break;
				case EQ:
					predicates.add(builder.equal(expression, filter.value));
					break;
				case NOTEQ:
					predicates.add(builder.notEqual(expression, filter.value));
					break;
				case LIKE:
					predicates.add(builder.like(expression, "%" + filter.value + "%"));
					break;
				case GT:
					predicates.add(builder.greaterThan(expression,(Comparable) filter.value));
					break;
				case LT:
					predicates.add(builder.lessThan(expression, (Comparable) filter.value));
					break;
				case GTE:
					predicates.add(builder.greaterThanOrEqualTo(expression, (Comparable) filter.value));
					break;
				case LTE:
					predicates.add(builder.lessThanOrEqualTo(expression, (Comparable) filter.value));
					break;
				case NULL:
					predicates.add(builder.isNull(expression));
					break;
				case NOTNULL:
					predicates.add(builder.isNotNull(expression));
					break;
				case IN:
					String val = String.valueOf(filter.value);
					predicates.add(expression.as(String.class).in(new ArrayList<String>(Arrays.asList(val.split(",")))));
					break;
				default:
					break;
			}
		}
		return predicates;
	}
	/**
	 * 获取Predicate集合
	 * @param filters 可以是通过request获取的Collection<SearchFilter>对象
	 * @param root 
	 * @param builder
	 * @return
	 */
	public static <T> List<Predicate> getPredicates(final Collection<SearchFilter> filters,Root<T> root, CriteriaBuilder builder){
		List<Predicate> predicates = new ArrayList<>();
		if (filters != null && filters.size() > 0) {
			for (SearchFilter filter : filters) {
				// nested path translate, 如Task的名为"user.name"的filedName, 转换为Task.user.name属性
				String[] names = StringUtils.split(filter.fieldName, ".");
				if( filter.fieldName.startsWith("@")){//多对多或1对多的情况
					String manyName =StringUtils.split(filter.fieldName, "@")[0];
					names = StringUtils.split(manyName, "#");
					String taxon = names[0];//此处应该为list的类名 如:查用户时部门的类名
					String nature = names[1];//此处应该为list的属性名 如:查用户时用户中部门的属性名
					String taxonNature = names[2];//此处应该为list的类的属性名 如：查用户时的部门的id

					ListJoin orgIdP= root.join(root.getModel().getList(nature,SpringContextHolder.getBean(taxon).getClass()),JoinType.LEFT);
					if(names.length>3){
						predicates = DynamicSpecifications.addPredicate(filter, builder, predicates, null,orgIdP,taxonNature,names[3]);
					}else{
						predicates = DynamicSpecifications.addPredicate(filter, builder, predicates, null,orgIdP,taxonNature,null);
					}
					continue;
				}
				Path expression = root.get(names[0]);//多对1的情况
				for (int i = 1; i < names.length; i++) {
					expression = expression.get(names[i]);
				}
				predicates = DynamicSpecifications.addPredicate(filter, builder, predicates, expression,null,null,null);
			}
		}
		return predicates;
	}
}
